package org.meb.conquest.rest.core;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.Transformer;
import org.meb.conquest.db.model.Deck;
import org.meb.conquest.rest.auth.AuthUser;
import org.meb.conquest.rest.json.CardLoader;
import org.meb.conquest.rest.json.DeckLoader;
import org.meb.conquest.rest.json.JsonUtils;
import org.meb.conquest.rest.json.model.JsonCard;
import org.meb.conquest.rest.json.model.JsonDeck;
import org.meb.conquest.rest.json.model.JsonDeckMember;
import org.meb.conquest.rest.json.model.JsonError;
import org.meb.conquest.rest.service.DeckService;
import org.meb.conquest.rest.service.QueryContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;

@Path("/deck")
public class DeckController {

	private static final Logger log = LoggerFactory.getLogger(DeckController.class);

	@Inject
	private QueryContext queryContext;

	@Inject
	private DeckService deckService;

	@Inject
	private DeckLoader deckLoader;

	@Inject
	private CardLoader cardLoader;

	@Inject
	private AuthUser authUser;

	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public Response loadDecks(@QueryParam("language") String language) {
		log.info("loadDecks(): auth user: {}", authUser);

		queryContext.setUserId(authUser.getUserId());
		queryContext.setUserLanguage(language);

		try {
			String json = JsonUtils.write(deckLoader.loadDecks());
			return Response.ok(json).build();
		} catch (IOException e) {
			log.error("Unable to load decks", e);
			return Response.serverError().build();
		}
	}

	@GET
	@Path("/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public Response loadDeck(@PathParam("id") Long id, @QueryParam("language") String language) {
		log.info("getDecks(): auth user: {}, id: ", authUser, id);

		queryContext.setUserId(authUser.getUserId());
		queryContext.setUserLanguage(language);

		try {
			JsonDeck jsonDeck = deckLoader.loadDeck(id, true);
			fillAllWarlordDeckCards(jsonDeck);
			return Response.ok(JsonUtils.write(jsonDeck)).build();
		} catch (IOException e) {
			log.error("Unable to load deck", e);
			return Response.serverError().build();
		}
	}

	@POST
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	public Response insertDeck(@QueryParam("language") String language, String body) {
		queryContext.setUserId(authUser.getUserId());
		queryContext.setUserLanguage(language);

		try {
			Deck deck = JsonUtils.readObject(body, JsonDeck.class).toDeck();
			deck = deckService.saveUserDeck(null, deck);
			JsonDeck jsonDeck = new JsonDeck(deck, true);
			fillAllWarlordDeckCards(jsonDeck);
			return Response.ok(JsonUtils.write(jsonDeck)).build();
		} catch (Exception e) {
			long timestamp = System.currentTimeMillis();
			String message = "Unable to insert deck [user id: {}, timestamp: {}]";
			logError(message, authUser.getUserId(), timestamp, e);
			return Response.serverError().entity(new JsonError(timestamp)).build();
		}
	}

	@PUT
	@Path("/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	public Response updateDeck(@PathParam("id") Long id, @QueryParam("language") String language, String body) {
		queryContext.setUserId(authUser.getUserId());
		queryContext.setUserLanguage(language);

		try {
			Deck deck = JsonUtils.readObject(body, JsonDeck.class).toDeck();
			deck = deckService.saveUserDeck(id, deck);
			JsonDeck jsonDeck = new JsonDeck(deck, true);
			fillAllWarlordDeckCards(jsonDeck);
			return Response.ok(JsonUtils.write(jsonDeck)).build();
		} catch (Exception e) {
			long timestamp = System.currentTimeMillis();
			String message = "Unable to update deck [user id: {}, timestamp: {}]";
			logError(message, authUser.getUserId(), timestamp, e);
			return Response.serverError().entity(new JsonError(timestamp)).build();
		}
	}

	@DELETE
	@Path("/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public Response deleteDeck(@PathParam("id") Long id, @QueryParam("language") String language) {
		queryContext.setUserId(authUser.getUserId());
		queryContext.setUserLanguage(language);

		try {
			deckService.deleteUserDeck(id);
			return Response.ok("{}").build();
		} catch (Exception e) {
			long timestamp = System.currentTimeMillis();
			String message = "Unable to delete deck [user id: {}, timestamp: {}]";
			logError(message, authUser.getUserId(), timestamp, e);
			return Response.serverError().entity(new JsonError(timestamp)).build();
		}
	}

	@GET
	@Path("/export/octgn/{id}")
	@Produces(MediaType.TEXT_PLAIN)
	public Response exportDeckToOctgn(@PathParam("id") Long id, @QueryParam("language") String language) {
		queryContext.setUserId(authUser.getUserId());
		queryContext.setUserLanguage(language);

		try {
			ExportedDeck exported = deckService.exportUserDeck(id, ExportType.OCTGN);
			ResponseBuilder rs = Response.ok(exported.getData());
			rs.header("Content-Disposition", "attachment; filename=\"" + exported.getTechName() + ".o8d\"");
			return rs.build();
		} catch (Exception e) {
			long timestamp = System.currentTimeMillis();
			String message = "Unable to delete deck [user id: {}, timestamp: {}]";
			logError(message, authUser.getUserId(), timestamp, e);
			return Response.serverError().entity(new JsonError(timestamp)).build();
		}
	}

	private void fillAllWarlordDeckCards(JsonDeck jsonDeck) {
		List<JsonDeckMember> members = jsonDeck.getMembers();
		HashMap<Long, JsonDeckMember> membersMap = new HashMap<Long, JsonDeckMember>();
		MapUtils.populateMap(membersMap, members, new Transformer<JsonDeckMember, Long>() {

			@Override
			public Long transform(JsonDeckMember member) {
				return member.getCardId();
			}

		});

		List<JsonCard> warlordDeckCards = cardLoader.loadWarlordDeckCards(jsonDeck.getWarlordId());
		for (JsonCard card : warlordDeckCards) {
			JsonDeckMember member = membersMap.get(card.getId());
			if (member == null) {
				member = new JsonDeckMember();
				member.setCardId(card.getId());
				member.setQuantity(0);
				members.add(member);
			}
			// Integer maxQuantity; = Math.min(arg0, arg1)card.getQuantity();
			// member.setMaxQuantity(maxQuantity);
		}
	}

	@SuppressWarnings("unused")
	private void logError(String message, Object[] params, Exception e) {
		message = MessageFormatter.format(message, params).getMessage();
		log.error(message, e);
	}

	private void logError(String message, Object param0, Object param1, Exception e) {
		message = MessageFormatter.format(message, param0, param1).getMessage();
		log.error(message, e);
	}
}